/*******************************************************************************
 * Copyright (c) 2007 University of Oslo.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     University of Oslo - initial API and implementation
 *******************************************************************************/

package com.cea.papyrus.diagram.sequence.sedi.edit.handlers;

import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.gef.EditPolicy;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.requests.CreateRequest;
import org.eclipse.uml2.uml.ActionExecutionSpecification;
import org.eclipse.uml2.uml.BehaviorExecutionSpecification;
import org.eclipse.uml2.uml.ExecutionSpecification;
import org.eclipse.uml2.uml.Interaction;
import org.eclipse.uml2.uml.Lifeline;

import com.cea.papyrus.core.utils.Debug;
import com.cea.papyrus.diagram.sequence.sedi.edit.command.AnalyseOwnerCommand;
import com.cea.papyrus.diagram.sequence.sedi.edit.command.DecideExecutionOccurrenceBoundsCommand;
import com.cea.papyrus.diagram.sequence.sedi.edit.command.SelectPartCommand;
import com.cea.papyrus.diagram.sequence.sedi.model.command.CreateExecutionSpecificationCommand;
import com.cea.papyrus.diagram.sequence.sedi.model.command.CreateGraphicalElementCommand;
import com.cea.papyrus.diagram.sequence.sedi.model.command.SetConstraintCommand;
import com.cea.papyrus.diagram.sequence.sedi.model.command.SetOwnerCommand;
import com.cea.papyrus.diagram.sequence.sedi.model.util.ModelUtil;
import com.cea.papyrus.diagraminterchange2.di2.GraphNode;

/**
 * Handles creation of {@link ExecutionSpecification}.
 * This may be used to create :
 * - {@link ActionExecutionSpecification}
 * - {@link BehaviorExecutionSpecification}
 */
public abstract class ExecutionSpecificationCreateHandler extends InteractionFragmentCreateHandler {

	public ExecutionSpecificationCreateHandler(final EditPolicy hostPolicy) {
		super(hostPolicy);
	}

	@SuppressWarnings("unchecked")
	public Command[] getCreateCommands(final CreateRequest request) {
		
		boolean isInitialised = initialize(request);
		if (! isInitialised) {
			Debug.log(new Exception("Command initialization error."));
			return null;
		}
		
		// For a better readability, following commands are declared in the order of execution
		// (except "prepareXXXCmd" command called between main commands that are declared the main
		// Commands declaration.
		
		// Prepare UML elements creation commands
		final CreateExecutionSpecificationCommand createCmd = 
			getExecutionSpecCreateCommand(bestCoveredLifeline, interaction);

		// Create the graphicalElement
		final CreateGraphicalElementCommand createGECmd = new CreateGraphicalElementCommand();
		createGECmd.setAnyEditPart(interactionEP);
		createGECmd.setParentGE(interactionNode);

		// Calculate and set the size and position of the ExecutionSpecification according
		// to the creation area chosen by the user
		final DecideExecutionOccurrenceBoundsCommand analyseBoundsCmd = new DecideExecutionOccurrenceBoundsCommand();
		analyseBoundsCmd.setAnyEditPart(interactionEP);
		analyseBoundsCmd.setLifeline(bestCoveredLifeline);
		analyseBoundsCmd.setHint(new Rectangle(absolutLoc,
				request.getSize() != null ? request.getSize() : new Dimension(
						0, 0)));	
		final SetConstraintCommand setBoundsCmd = new SetConstraintCommand();

		// Analyse / Update the owner of the ExecutionSpecification
		// and its ExecutionOccurences
		// The owner is not systematically the Interaction, it may be a CombinedFragment
		// that enclose the graphical area where the ExecutionSpecification (or part of it)
		// is created.
		final AnalyseOwnerCommand analyseOwnerCmd = new AnalyseOwnerCommand();
		analyseOwnerCmd.setDiagram(diagram);
		analyseOwnerCmd.setAnyEditPart(interactionEP);
		final SetOwnerCommand setOwnerCmd = new SetOwnerCommand();
		final SetOwnerCommand setStartOwnerCmd = new SetOwnerCommand();
		final SetOwnerCommand setFinishOwnerCmd = new SetOwnerCommand();
				
		// Set selection on the created ExecutionSpecification
		final SelectPartCommand selectCmd = new SelectPartCommand();
		selectCmd.setAnyEditPart(interactionEP.getRoot());
		selectCmd.setSelectForEdit(false);

		// Following command are used to set main command parameters
		// prior to their execution
		
		final Command prepareCreateGECmd = new Command() {
			@Override
			public void execute() {
				createGECmd.setElement(createCmd.getExecutionSpecification());
			}
		};
		
		final Command prepareAnalyseBoundsCmd = new Command() {
			public void execute() {
				ExecutionSpecification eo = createCmd
				.getExecutionSpecification();

				analyseBoundsCmd.setExecutionSpecification(eo);
			}
		};
		
		final Command prepareSetBoundsCmd = new Command() {
			public void execute() {
				ExecutionSpecification executionSpecification = 
					createCmd.getExecutionSpecification();
				GraphNode executionSpecificationNode = ModelUtil.getGraphNode(executionSpecification, diagram);

				setBoundsCmd.setElement(executionSpecificationNode);
				setBoundsCmd.setBounds(analyseBoundsCmd.getPreferredBounds());
			}
		};
		
		final Command prepareAnalyseOwnerCmd = new Command() {
			public void execute() {
				analyseOwnerCmd.setLocation(analyseBoundsCmd
						.getPreferredBounds().getLocation());
			}
		};
		
		final Command prepareUpdateOwner = new Command() {
			public void execute() {
				
				ExecutionSpecification executionSpecification = 
					createCmd.getExecutionSpecification();
				
				setOwnerCmd.setFragment(executionSpecification);
				setOwnerCmd.setOwner(analyseOwnerCmd.getOwner());
				
				setStartOwnerCmd.setFragment(executionSpecification.getStart());
				setStartOwnerCmd.setOwner(analyseOwnerCmd.getOwner());
				
				setFinishOwnerCmd.setFragment(executionSpecification.getFinish());
				setFinishOwnerCmd.setOwner(analyseOwnerCmd.getOwner());
			}
		};
		
		final Command prepareSelectCmd = new Command() {
			public void execute() {
				ExecutionSpecification executionSpecification = 
					createCmd.getExecutionSpecification();

				selectCmd.setTarget(executionSpecification);
			}
		};
		
		// Return the resulting compound command
		return new Command[] { 
				createCmd, 
				prepareCreateGECmd, createGECmd,
				prepareAnalyseBoundsCmd, analyseBoundsCmd,
				prepareSetBoundsCmd, setBoundsCmd,
				prepareAnalyseOwnerCmd, analyseOwnerCmd,
				prepareUpdateOwner, setOwnerCmd, setStartOwnerCmd, setFinishOwnerCmd,
				prepareSelectCmd, selectCmd };
	}
	
	protected abstract CreateExecutionSpecificationCommand getExecutionSpecCreateCommand(Lifeline lifeline, Interaction interaction);
}
